#include "zlib.h"
#include <stdio.h>

int GZIPCompress(const void* data, unsigned int ndata, void* zdata, unsigned int* nzdata)
{
	int err = 0;
	z_stream c_stream;

	if (data && ndata > 0)
	{
		c_stream.zfree = NULL;
		c_stream.zalloc = NULL;
		c_stream.opaque = NULL;

		if (deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
		{
			return -1;
		}

		c_stream.avail_in = ndata;
		c_stream.avail_out = *nzdata;
		c_stream.next_in = (Bytef*)(data);
		c_stream.next_out = (Bytef*)(zdata);

		while (c_stream.avail_in && c_stream.total_out < *nzdata)
		{
			if (deflate(&c_stream, Z_NO_FLUSH) != Z_OK) return -1;
		}

		if (c_stream.avail_in) return c_stream.avail_in;

		while (1)
		{
			if ((err = deflate(&c_stream, Z_FINISH)) == Z_STREAM_END) break;

			if (err != Z_OK) return -1;
		}

		if (deflateEnd(&c_stream) != Z_OK) return -1;

		*nzdata = c_stream.total_out;

		return 0;
	}

	return -1;
}

int GZIPDecompress(const void* zdata, unsigned int nzdata, void* data, unsigned int* ndata)
{
	int err = 0;
	z_stream d_stream = {0};
	static char dummy_head[2] = { 0x8 + 0x7 * 0x10, (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF };

	d_stream.avail_in = 0;
	d_stream.zfree = NULL;
	d_stream.zalloc = NULL;
	d_stream.opaque = NULL;
	d_stream.next_out = (Bytef*)(data);
	d_stream.next_in = (Bytef*)(zdata);

	if (inflateInit2(&d_stream, MAX_WBITS + 16) != Z_OK) return -1;

	while (d_stream.total_out < *ndata && d_stream.total_in < nzdata)
	{
		d_stream.avail_in = d_stream.avail_out = 1;

		if ((err = inflate(&d_stream, Z_NO_FLUSH)) == Z_STREAM_END) break;

		if (err != Z_OK)
		{
			if (err != Z_DATA_ERROR) return -1;

			d_stream.next_in = (Bytef*)(dummy_head);
			d_stream.avail_in = sizeof(dummy_head);

			if ((err = inflate(&d_stream, Z_NO_FLUSH)) != Z_OK) return -1;
		}
	}

	if (inflateEnd(&d_stream) != Z_OK) return -1;

	*ndata = d_stream.total_out;

	return 0;
}